#include <windows.h>
#include <iostream>
#include <fstream>
#include <map>
#include <vector>
#include <string>

struct vertex2f
{
	float u;
	float v;
	vertex2f(){u=0.0f; v=0.0f;}
};

struct texCoord2f
	//Stores texture coords for a texture in a texture atlas
{
	vertex2f btmLft;
	vertex2f btmRgt;
	vertex2f topRgt;
	vertex2f topLft;
	int TexIndex;
	texCoord2f()
	{
		TexIndex = -1;
		//Initalise to -1 because 0 is a valid index
		//Into the texture vector, and because if we
		//Try to bind texture[-1], we get an error
		//Which is useful; It forces us to set a value
		//For it.
	}
};

std::map<int, texCoord2f> TexCoords;

struct TexParseData
{
	int index;
	std::string TexFile;
	std::string TexName;
	texCoord2f TexCoords;
};

void	TrimString(std::string& Str)
{
	while(!Str.empty() && (Str[0] == ' ' || Str[0] == '\t')){Str.erase(0, 1);}
};

std::string	GetSubStr(std::string& Str, char Seperator)
{
	unsigned int i = Str.find(Seperator); if(i == Str.npos || i == 0){return "";}
	std::string ReturnString = Str.substr(0, i);
	Str.erase(0, i + 1); TrimString(Str);
	return ReturnString;
};

int LoadTexture(const char *File)
{
	std::cout << "Loaded " << File << "! \n";
	return 0;
};


int LoadTextureAtlases(char *Catalogue)
{
	std::vector<std::string> TaiFiles;	//Stores the names of all the .tai files to process
	std::map<std::string, int> TexIDs;	//Stores the ID of each texture, along with the filename
	std::map<std::string, int> TexIndexes;	//Stores the indexes into texture[] of each one loaded, along with the filename
	std::map<std::string, bool> LoadedTextures;	//Keys are filenames of texures, value is whether it's loaded or not.
		
	std::ifstream CatalogueFile(Catalogue);	
	if(!CatalogueFile.good()){return false;}	//If the file doesn't open properly, return False
	//Tell ifstream that we want it to raise exceptions if something goes wrong
		CatalogueFile.seekg(0, std::ios::beg);
		std::string BufferStr;
		while(std::getline(CatalogueFile, BufferStr))	//eof becomes true when there's no more to read
		{
			TrimString(BufferStr);	
			//Remove whitespace at the start of the line. Stop if the string is empty
			if(BufferStr.empty()){continue;}	//If the line was empty, go to the next one
			if(BufferStr[0] == '#'){continue;}	//If the line was a comment, go to the next one
			if(BufferStr.size() > 5 && BufferStr.substr(0, 5) == "using")
			{
				BufferStr.erase(0,5); TrimString(BufferStr);
				//Remove the 'using' piece, and any whitespace that follows
				if(!BufferStr.empty()){TaiFiles.push_back(BufferStr);} 
				//And put the Tai file name into the list if the string isn't empty
				continue;
				//Now go to the next one
			}
			int i = strspn(BufferStr.c_str(), "1234567890");
			if(i)	//If the string begins with a numeric value
			{
				int Tex = atoi(BufferStr.substr(0, i).c_str());
				BufferStr.erase(0, i);
				BufferStr.erase(0, strspn(BufferStr.c_str(), " ,="));	//Remove delimiting characters
				TexIDs[BufferStr] = Tex;	//Set the name associated with the texID to the remaining characters
				continue;
			}

		}
		CatalogueFile.close();
	std::ifstream TaiFile;

	for(unsigned int i = 0; i < TaiFiles.size(); i++)
	{
		TaiFile.open(TaiFiles[i].c_str());
		if(!TaiFile.good()){continue;}	//If the file doesn't open properly, try the next one
		std::cout << "Reading Tai File " << i <<"\n";
			TaiFile.seekg(0, std::ios::beg);
			while(getline(TaiFile, BufferStr))
			{
				TexParseData currentTex; 
				TrimString(BufferStr);	
				//Remove whitespace at the start of the line. Stop if the string is empty
				if(BufferStr.empty()){continue;}	//If the line was empty, go to the next one
				if(BufferStr[0] == '#'){continue;}	//If the line was a comment, go to the next one
				//it's not a comment, so it's a filename followed by two tabs; So, we look for the tabs.
				
				currentTex.TexName = GetSubStr(BufferStr, '\t');

				if(TexIDs.find(currentTex.TexName) == TexIDs.end()){continue;}
				//If this texture isn't required, skip over it.
				currentTex.TexFile = GetSubStr(BufferStr, ',');
				currentTex.index = atoi(GetSubStr(BufferStr, ',').c_str());
				GetSubStr(BufferStr, ',');
				//Ignore texture type; should always be 2d
				currentTex.TexCoords.btmLft.u = atof(GetSubStr(BufferStr, ',').c_str());
				currentTex.TexCoords.btmLft.v = atof(GetSubStr(BufferStr, ',').c_str());
				GetSubStr(BufferStr, ',');
				//Ignore Depth; Should always be 0, and is only important for 3d textures.
				float width = atof(GetSubStr(BufferStr, ',').c_str());
				float height = atof(BufferStr.c_str());
				//BufferStr should now be just the last value, which is not followed by any delimiting characters
				
				currentTex.TexCoords.btmRgt.u = currentTex.TexCoords.btmLft.u + width;
				currentTex.TexCoords.btmRgt.v = currentTex.TexCoords.btmLft.v;
				currentTex.TexCoords.topLft.u = currentTex.TexCoords.btmLft.u;
				currentTex.TexCoords.topLft.v = currentTex.TexCoords.btmLft.v + height;
				currentTex.TexCoords.topRgt.u = currentTex.TexCoords.btmLft.u + width;
				currentTex.TexCoords.topRgt.v = currentTex.TexCoords.btmLft.v + height;

				if(!LoadedTextures[currentTex.TexFile])
				{
					TexIndexes[currentTex.TexFile] = LoadTexture(currentTex.TexFile.c_str());
					LoadedTextures[currentTex.TexFile] = true;
				}
				//If the texture isn't loaded, load it and set the maps so that we'll know that it's loaded
				//If we look for it again.

				currentTex.TexCoords.TexIndex = TexIndexes[currentTex.TexFile];
				TexCoords[TexIDs[currentTex.TexName]] = currentTex.TexCoords;
				std::cout << currentTex.TexName << " is in " << currentTex.TexCoords.TexIndex <<"\n";
				std::cout << "    Tex Coords are (" << currentTex.TexCoords.btmLft.u << ", " << currentTex.TexCoords.btmLft.v << ") to (" << currentTex.TexCoords.topRgt.u << ", " << currentTex.TexCoords.topRgt.v << ")\n";
			}
			TaiFile.close();
			TaiFile.clear();
			//Clear any error attributes set
	}
	return 0;
};

int main (int argc, char ** argv[])
{
	std::cout << "Loading TestAtlas.cat \n";
	LoadTextureAtlases("TestAtlas.cat");
	std::cout << "Done!\n";
	std::cin.get();
	return 0;
};
